<?php
/**
 * 访问者模式
 *
 * 适用于数据结构未定的系统,缺点是访问者必须为每一个结构设置一个方法
 */
//访问者抽象类
abstract class Visitor{
	//访问结构A时使用的方法
	abstract public function VisitConcreteElementA(ConcreteElementA $ConcreteElementA);
	//访问结构B时使用的方法
	abstract public function VisitConcreteElementB(ConcreteElementB $ConcreteElementB);
}
//元素抽象类
abstract class Element{
	abstract public function Accept(Visitor $visitor);
}



//访问者A
class ConcreteVisitorA extends Visitor
{
	public function VisitConcreteElementA(ConcreteElementA $ConcreteElementA)
	{
		echo get_class($ConcreteElementA)." visited by ".get_class($this).PHP_EOL;
	}
	public function VisitConcreteElementB(ConcreteElementB $ConcreteElementB)
	{
		echo get_class($ConcreteElementB)." visited by ".get_class($this).PHP_EOL;
	}
}
//访问者B
class ConcreteVisitorB extends Visitor
{
	public function VisitConcreteElementA(ConcreteElementA $ConcreteElementA)
	{
		echo get_class($ConcreteElementA)." visited by ".get_class($this).PHP_EOL;
	}
	public function VisitConcreteElementB(ConcreteElementB $ConcreteElementB)
	{
		echo get_class($ConcreteElementB)." visited by ".get_class($this).PHP_EOL;
	}
}

//元素A
class ConcreteElementA extends Element
{
	public function Accept(Visitor $visitor)
	{
		$visitor->VisitConcreteElementA($this);
	}
	//商业方法
	public function OperationA()
	{

	}
}
//元素B
class ConcreteElementB extends Element
{
	public function Accept(Visitor $visitor)
	{
		$visitor->VisitConcreteElementB($this);
	}
	//商业方法
	public function OperationB()
	{

	}
}
//元素链
class ObjectStructure
{
	private $elements = array();
	//添加元素
	public function Attach(Element $element)
	{
		$other_list = array($element);
		//获取交集
		$result_list = array_uintersect($this->elements,$other_list,array(__CLASS__,"key_compare_func"));
		//如果没有，则加入该元素
		if(empty($result))
		{
			array_push($this->elements,$element);  
		}
	}
	//删除元素
	public function Detach($key)
	{
		if(isset($this->elements[$key]))
		{
			array_splice($this->elements,$key,1);
		}	
	}
	//比较的方法
	public function key_compare_func($a,$b)
	{
		if(is_object($a)&&is_object($b)&&$a === $b){
			return 0;
		}
		return ($a > $b)? 1 :-1;
	}
	public function Accept(Visitor $visitor)
	{
		foreach($this->elements as $element)
		{
			$element->Accept($visitor);
		}
	}
}

//元素链创建
$ObjectStructure = new ObjectStructure();
$ObjectStructure->Attach(new ConcreteElementA());
$ObjectStructure->Attach(new ConcreteElementB());
$ObjectStructure->Attach(new ConcreteElementB());
//访问者创建
$ConcreteVisitorA = new ConcreteVisitorA();
$ConcreteVisitorB = new ConcreteVisitorB();
//元素链接收访问者
$ObjectStructure->Accept($ConcreteVisitorA);
$ObjectStructure->Accept($ConcreteVisitorB);

